perm filename 106A02[1,RWF]1 blob
sn#732984 filedate 1983-11-15 generic text, type C, neo UTF8
COMMENT ⊗ VALID 00012 PAGES
C REC PAGE DESCRIPTION
C00001 00001
C00003 00002 This is an algorithm (though fuzzy around the edges) for designing a program
C00009 00003 ←Reading Programs←
C00010 00004 Conditional Commands
C00011 00005 ***** *****
C00013 00006 Again, we change the WRITE('B') to
C00016 00007 If we change the character constants 'A', 'D', and 'B' to asterisks, and
C00019 00008 ←OUTPUT and output.←
C00021 00009 Output Format
C00024 00010 The Most Unforgettable Characters I Have Ever Met
C00033 00011 Do-It-Yourself Input/Output
C00040 00012 Cleveland Budgets Left a Mess By One Small Computer Goof
C00048 ENDMK
C⊗;
This is an algorithm (though fuzzy around the edges) for designing a program
using the TOPS 20 system and the PASGO compiler at LOTS.
(1) Design an algorithm for the problem.
(2) Express the algorithm as a program in Pascal. Assume that it contains
errors. (It does.)
(3) Create a file containing the Pascal program. Give it a name consisting of
up to six letters followed by ".PGO"; the part before the period can be the
problem number of an assignment, like or "P08" or an abbreviated description
description, like "DRAWEX". Let's say the name is "FFF.PGO." Use
@CREATE FFF.PGO↓.
(4) Print a hard (paper) copy of the program, by @ PRINT FFF.PGO↓.
(5) Desk-check the hard copy.
(6) Prepare an input data file if needed. Call it "FFF.DAT".
(7) Try the program out by the TOPS-20 command
@ EXECUTE FFF.PGO↓
(8) If there are syntax error messages from the compiler, determine the reasons
for them (at least the first one; sometimes a single error initiates a long
sequence of largely unintelligible subsequent messages, if the compiler makes
the wrong choices in recovering from the first error.) Repair them
(use @ EDIT FFF.PGO↓) and return to step (7).
(9) If there are no syntax errors, as evidenced by the appearance of something like
"EXECUTION STARTING" on the terminal screen, the program wll begin by asking
you for the TOPS-20 names of any files (eg. INPUT and OUTPUT) your Pascal
program uses. The usual names are "FFF.DAT↓" and "FFF.OUT↓" respectively,
for a program called FFF.PGO.
(10) If the program fails to run to completion, you will get an error message.
See for a list of error messages and their interpretation. Such an
error during execution means, ordinarily, that you have applied an operation
to an argument that lies outside the correct range, like SQRT(-3.7). Locate
and correct the error (@EDIT FFF.PGO↓) and return to step 7. If the program
has not stopped in one minute, it is probably trying to run forever, or for
several thousand years. Stop the program with CONTROL-C, locate the
non-terminating iteration, correct it, and return to step 7.
(11) If the program runs to completion, it will return control to the executive,
displaying the @ on the terminal. Now the results are on the file "FFF.OUT".
(12) Inspect your results on the terminal, if you think they will fit on the
screen, by @TYPE FFF.OUT↓. Print them and the program with
@PRINT FFF.OUT,FFF.PGO↓, and get them from the printer. Since LOTS is a
self-service system, you are as responsible as anyone else for keeping the
printer clear of paper jams and supplied with paper, ribbons, and tender
loving care; inform yourself about these things ASAP. If the results are
unbelievable, find and correct the logical errors in your program and return
to step 7.
(13) You are finished. Release the terminal for someone else to use, by
@LOGOUT↓. This action is very much in your interest as well as in the
interest of anyone waiting. Failing to do so is like leaving your headlights
on when you park your car, with the keys in the ignition. Whenever you don't
know what you are doing, your best practice to conserve your time allocation
is to log out and spend time desk checking.
←Reading Programs←
No textbook can hint at all the methods programmers use in their programs.
We recommend that you read every Pascal program that you can legitimately
lay your hands on. Sources include other textbooks (watch out, though:
many contain erors, unlike this one), class handouts of this and other
courses, and the LOTS directory <CS10X>. You might ask more experienced
programmers if you can read some of their programs, but please don't browse
without asking.
Conditional Commands
If you really loved me you'd lose weight.
If you really loved me you wouldn't mind my weight.
If wishes were horses, beggars would ride.
If there is no God, then everything is permitted.
If we had ham, we'd have ham and eggs, if we had eggs.
***** *****
****** *****
******* *****
******** *****
********* *****
***************
***** *********
***** ********
***** *******
***** ******
***** *****
How can we design a program to print the letter "N" as shown above? First, let's
print out a block of A's the same size as the N. (Later we will change the program
so that some of the A's become *'s and some become blank spaces).
FOR R:= 1 TO 11 DO
BEGIN
FOR C:= 1 TO 15 DO
WRITE('A');
WRITELN
END
AAAAAAAAAAAAAAA
AAAAAAAAAAAAAAA
AAAAAAAAAAAAAAA
AAAAAAAAAAAAAAA
AAAAAAAAAAAAAAA
AAAAAAAAAAAAAAA
AAAAAAAAAAAAAAA
AAAAAAAAAAAAAAA
AAAAAAAAAAAAAAA
AAAAAAAAAAAAAAA
AAAAAAAAAAAAAAA
Now we change the program so that everything in columns 11 to 30 is a B, rather
than an A. We do this by changing WRITE('A') to
IF C<= 5 THEN WRITE('A') ELSE WRITE('B')
AAAAABBBBBBBBBB
AAAAABBBBBBBBBB
AAAAABBBBBBBBBB
AAAAABBBBBBBBBB
AAAAABBBBBBBBBB
AAAAABBBBBBBBBB
AAAAABBBBBBBBBB
AAAAABBBBBBBBBB
AAAAABBBBBBBBBB
AAAAABBBBBBBBBB
AAAAABBBBBBBBBB
Again, we change the WRITE('B') to
IF C>= 11 THEN WRITE('B') ELSE WRITE('C')
AAAAACCCCCBBBBB
AAAAACCCCCBBBBB
AAAAACCCCCBBBBB
AAAAACCCCCBBBBB
AAAAACCCCCBBBBB
AAAAACCCCCBBBBB
AAAAACCCCCBBBBB
AAAAACCCCCBBBBB
AAAAACCCCCBBBBB
AAAAACCCCCBBBBB
AAAAACCCCCBBBBB
The whole program is now
FOR R:= 1 TO 11 DO
BEGIN
FOR C:= 1 TO 15 DO
IF C<= 5 THEN WRITE('A')
ELSE IF C>= 11 THEN WRITE ('B')
ELSE WRITE('C')
WRITELN
END
Now we change WRITE('C') to
IF C>= 5+R THEN WRITE('C') ELSE WRITE('D')
AAAAACCCCCBBBBB
AAAAADCCCCBBBBB
AAAAADDCCCBBBBB
AAAAADDDCCBBBBB
AAAAADDDDCBBBBB
AAAAADDDDDBBBBB
AAAAADDDDDBBBBB
AAAAADDDDDBBBBB
AAAAADDDDDBBBBB
AAAAADDDDDBBBBB
AAAAADDDDDBBBBB
and finally change WRITE('D') to
IF C>= R THEN WRITE('D') ELSE WRITE ('E').
AAAAACCCCCBBBBB
AAAAADCCCCBBBBB
AAAAADDCCCBBBBB
AAAAADDDCCBBBBB
AAAAADDDDCBBBBB
AAAAADDDDDBBBBB
AAAAAEDDDDBBBBB
AAAAAEEDDDBBBBB
AAAAAEEEDDBBBBB
AAAAAEEEEDBBBBB
AAAAAEEEEEBBBBB
If we change the character constants 'A', 'D', and 'B' to asterisks, and
'C', 'E', to blanks, the program is
PROGRAM NF(OUTPUT);
VAR R,C:INTEGER:
BEGIN
FOR R:=1 TO 11 DO
BEGIN
FOR C:=1 TO 15 DO
IF C<=5 THEN WRITE('*')
ELSE IF C>=11 THEN WRITE('*')
ELSE IF C >=5+R THEN WRITE(' ')
ELSE IF C>=1+R THEN WRITE('*')
ELSE WRITE(' ');
WRITELN
END
END.
Exercise: Design an algorithm to print a letter `Z'.
←OUTPUT and output.←
The results printed by your programs go inot a file called OUTPUT. this is
only a temporary name for the file, used while your program is being
executed. When the program is finished, the file is saved in your
directory, under another name. Reasons for this system are:
(1) You may want to execute the same program several times, and keep all
the output files; they need distinct names.
(2) You may want to execute different programs, all of which call their
result files OUTPUT, and keep all the output files; again, they need
distinct names.
(3) The system for naming files varies from one computer to another. The
Pascal standard can't accomodate all of them. At LOTS, for example, a file
name has three parts, separated by periods; the third part is a number.
When you execute a Pascal program at LOTS, after translating the program
into machine language the executive program will ask
OUTPUT:
and wait for you to type the permanent name of your result file. I
normally use the program name, followed by a ".OUT".
Output Format
In a Pascal WRITE or WRITELN command, each operand is normally printed in a
standard format for the type of value it has. If the operand is an integer
variable, and the computer uses 36 biwords, the value of the variable is
printed as a decimal number of up to 11 digits, possibly preceded by a minus
sign. Including a blank space to separate adjacent numbers, we use a
13-character field, with the first character always blank, and the value of the
number right-justified so that it is easy to print columns of numbers with the
units digits lined up vertically.
If you know, however, that the value of variable I is positive, and no more than
(say) 4096, you may want to print it in four or five character regions, to save
space.
This can be done by using I:4 or I:5 as the operand of a WRITE or WRITELN command.
Any expression appearing as an operand may be followed by a colon and the desired
length of field. Usually the length is a constant, but any expression with an
integer value will do.
If the value won't fit in a field of the specified size, Pascal embodies the
assumption that the user prefers to get full information at the expense of
consistent appearance; the field is expanded to fit the operand. This can be
useful when you want to print numbers in the context of natural language; the
command WRITE('there are', I:1 'airplanes approaching') can result in
There are 2 airplanes approaching
or There are 2000000 airplanes approaching
The Most Unforgettable Characters I Have Ever Met
Pascal works with other types of data besides numbers and truth values. One
such type is the set of characters found on the typical computer terminal
keyboard. The set includes the digits 0-9, the letters a-z, A-Z in both cases
(minuscule and majuscule, if you want the precise names), all the familiar
punctuation marks, all the mathematical symbols used in Pascal, the space, and
a few other odds and ends like $ and @.
This type is called CHAR (which may as well be pronounced ``character'') in
Pascal. Like any fully equipped modern type, it has its own citizenry of
constants and variables, and an intra- and inter-type transportation system
of operators and functions, which can be combined in expressions for more
elaborate tours.
For the set of possible values of type CHAR, see Table C1, pg 440 of SW&P.
There is a literal constant for each one. With one exception, the literal
constant is the character itself, preceded and followed by apostrophes
('A', '2', ':'). The exception is the apostrophe itself; its constant
form is four apostrophes in a row ('''').
Symbolic constants can be defined in terms of the literal constants:
CONST LASTVOWEL='u';
Variables are of type CHAR if declared so; they may be assigned any values of
of that type. Here is a fragmentary program using only characters:
PROGRAM...
CONST COLON=':'; MYINITIAL='F';
VAR C,H : CHAR;
BEGIN
C:='A';
H:=MYINITIAL;
WRITE('B',COLON,H,C)
C:=H
WRITELN(C)
END (* output is B:FAF *)
Characters can be read to character variables from the INPUT file, or from
any text file or CHAR file. If
READ(C,H)
appeared in the above program, the next two characters of the INPUT file
would be read and assigned to C and H. At the end of a line, READ(C)
would read the end-of-line symbol (which is not a character in the
sense of belonging to CHAR) and assign a space to C.
The characters of CHAR have a conventional ordering on each computer, which
roughly corresponds to alphabetical order. There is also a conventional
numbering, giving each character the number of its place in the ordering.
We can use the operators <,>,=, and their combinations to test the relative
ordering of two characters. Standard Pascal requires that the capital letters
A-Z be in alphabetical order, and that the lower case letters be in alphabetical
order, (other characters may be interspersed!) To find out if the value of C,
already known to be a letter, is one of 'I','J','K','L','M', or 'N' in either
case, we could use
IF (C>='I')AND(C<='N')OR(C>='i')AND(C<='n').
Standard Pascal also requires that the digits 0-9, as characters, be in numerical
order and consecutive.
Here is program fragment which expects the word YES or NO on the INPUT file,
possibly preceded by spaces and abbreviated. It will read the whole word,
and choose between commands SY and SN.
READ(C);
WHILE C=' ' DO READ(C);
READ(H);
WHILE H<> ' ' DO READ(H);
IF C='Y' THEN SY
ELSE IF C='N' THEN SN
ELSE WRITE('INPUT OTHER THAN Y,N')
Standard functions with characters as arguments or as values include these:
ORD(C) is the ordinal number of the character C.
Tables in Appendix C of SW&P give the
most common ordinal numberings of
characters; you should find out which
is used on your computer.
In the ASCII ordering, used at LOTS,
letters are consecutive.
CHR(N) is the character (if any) whose ordinal
number is N. It is the inverse of ORD;
CHR(ORD(C))=C.
PRED(C) is the immediate predecessor of C in the
ordering; it is the same as CHR(ORD(C)-1).
If C is the first character in the ordering,
it is a mistake to use PRED(C).
SUCC(C) is the immediate successor of C in the ordering;
it is the same as CHR(ORD(C)+1). If C is the
last character in the ordering, it is a mistake
to use SUCC(C).
To print the upper case alphabet using ASCII, we can use either of the two
program fragments
(1) C:='A';
WHILE C<='Z' DO
BEGIN
WRITE(C)
C:=SUCC(C)
END
(2) FOR C:='A' TO 'Z' DO
WRITE(C)
The second program works because iteration variables may be of any type that
has an ordinal numbering, in particular of type CHAR.
We shall later encounter types which have as their values whole sequences of
characters, including words, sentences, formulas, etc; such sequences we call
strings. We have already encountered writing of string constants, consisting
of two or more characters enclosed in single quotes (apostrophes). For the
moment, the reader may treat writing of a string constant as an abbreviation
for writing the characters of the constant;
WRITE('ABC') abbreviates WRITE('A','B','C').
What can be done with characters? Only a limited amount, until we can work
with long strings of them. As an example above showed, they can be used as
convenient input codes to choose between actions. In output, strings of
characters provide titles, punctuation, etc. Within a computation, however,
characters are only a convenience; rather than work with characters, a program
could work with their ordinal numbers, so we can not expect to do anything with
characters we could not do without them.
Exercise: Write an iterative program to print the consonants.
Exercise: Write a program to read a roman numeral and print its
value in decimal.
(Hint: the value of letter is negative if the next
letter has a larger value.)
Examples of roman numerals: III = 3, VI = 6, IX = 9, LIV = 54,
XCIX = 99, MCMLXXXIII = 1983, D = 500.
Exercise: Write a program to count to M in roman numerals.
Do-It-Yourself Input/Output
Reading and printing of decimal numbers is not a primitive operation on most
computers, so Pascal provides standard subalgorithms to handle most of the
common situations that arise. Now and then, however, a problem comes up
where the built-in mechanisms are inadequate, and the programmer must
explicitly handle the individual decimal digits of numbers moving to or
from files. Suppose we have a number N, known to be greater than 100,000
and less than 1,000,000, which we want to print punctuated with a comma
after the thousands digit, like `123,456'. The WRITE command for numbers
does not provide for this form, so the program will have to execute
WRITE(','), preceded by something that writes out (say) 123, and followed by
something that writes 456.
We can calculate the numbers to be printed before and after the comma as N
DIV 1000 and N mod 1000 repectively, and if N is 123456, the command
WRITE(N DIV 1000:3,',',N MOD 1000:3) prints
123,456
as desired. If N is 123004, however, we get an unpleasant surprise:
123, 4
because the WRITE command never prints leading zeroes. For the part of the
number that follows the comma, at least, we must work out the individual
digits, and print them separately. To get the tens digit requires two
steps. Taking N MOD 100 gives us the numerical value of the two right hand
digits (56 and 4 in the examples above), after which a division by 10 gives
the tens digit. To print a six digit integer N withut supressing leading
zeroes can be done with this subprogram:
D:=1000000;
FOR I:= 1 TO 6 DO
BEGIN
D:=D DIV 10; (*FIRST TIME 100000, LAST TIME 1*)
Q:=N DIV D; (*NEXT DIGIT OF N*)
WRITE(Q:1);
N:=N MOD D
END
where a comma can be inserted by inserting IF I=3 THEN WRITE (',') after
the WRITE command.
Exercise
If we don't know that N is greater than 100,000, a more complicated
algorithm is needed. Write a program that will print N in one of these
forms:
1,234,567
123,456
12,345
1,234
123
12
1
0
(Solution: make 0 a special case, otherwise remember if there has been a
non-zero digit, supressing all printing until a non-zero digit has been
encountered.)
Suppose we want to read integers containing commas, ignoring the commas,
and stopping at the first blank space. Reading into an integer variable
can't handle the task, so we have to read individual characters, building
up the value of the number from the values of the indivdiual characters.
While the ordinal values of the digit characters are not the same as their
numerical values (this is because the digits are not the first characters
in standard alphabetical order), they are consecutive, so we can read a
digit into a CHAR variable and get its numerical value DIGIT by
READ (C);
DIGIT:= ORD(C) - ORD('O').
We get the value of a multi-digit member like 123456 from the values of its
individual digits by treating it as a polynomial
1 X 10↑5 + 2 X 10↑4 + 3 X 10↑3 + 4 X 10↑2 + 5 X 10 + 6,
or more simply
((((1 X 10 + 2) X 10 + 3) X 10 + 40 X 10+ 5) X 10 + 6.
Now the plan of the algorithm becomes clear. We read the characters of the
number, discarding commas, and keeping track of the numerical value of the
part of the number so far seen:
N:=0;
READ(C);
WHILE C <> ` ' DO
BEGIN
IF C <> `,' THEN
N:=N * 10 + ORD(C) - ORD('O');
READ(C);
END;
(*N CONTAINS THE NUMBER READ*)
This program is not fussy about what it reads. If the first input character is
blank, it treats that as a way of writing 0. If the input string is ,,35,0,
it treats that as a way of writing 350. A better program would watch for such
inputs as possible errors.
_Exercise_
The subprogram above fails if the number read is close to the maximum integer
value MAXINT. Revise it so it can read any positive integer up to MAXINT.
_Exercise_
Revise it to detect an attempt to read a number larger than MAXINT. Why is
IF N > MAXINT THEN WRITE ('TOO BIG')
not a good approach?
_Exercise_
Revise the reading algorithm to read integers written in base 16, with the
letters A through F serving as digits with values 10 through 15.
Cleveland Budgets Left a Mess By One Small Computer Goof
Cleveland
Someone in the county auditor's office listed the worth of
a home owned by Joseph and Elizabeth Sprenger at $510
million. The valuation plunged the city's budget and the
schools' budget into confusion.
The house was worth $35,000.
As a result, Cleveland will get $2 million less in real
estate revenues than expected and the city's schools will
get $4 million less. Both had based their budgets on
anticipated revenues that included the auditor's error.
``It throws our whole budget out of whack,'' said Joseph
Tegreene, a member of the school board. ``We were proud
we had produced the first balanced budget in five years
without a state loan.''
It was not known who made the error.
Cuyahoga County Auditor Tim McCormack said someone at a
computer console apparently placed the `5100' code number
used to indicate residential homes at the front of the
value for the Sprengers' home. The value came out
$510,035,000.
The Sprengers never got a tax bill on the erroneous value.
It was corrected before being sent out, but McCormack said
no one notified the schools.
Tegreene said he expects the school board to try to trim
$4 million from its budget. Phillip Allen, city budget
director, said the city will do new projections on its
budget.
---San Francisco Chronicle, June 24, 1983
``Who's buried in downtown Cleveland?''
``Everybody.''
Another Cleveland Joke
Robert W. Floyd
Copyright 1983
Don't blame the typist who entered the incorrect value of the Sprenger home into
the computer. Typists always make mistakes; a well-designed information
processing system takes that into account, and detects or allows for the mistakes.
Don't blame the computer, either. It was just following orders, and, unlike
Adolph Eichmann, it had no choice in the matter. I can imagine it muttering to
itself ``This don't seem right somehow. I wish I had some way to ask somebody
about it.'' A good computer programmer always designs his programs to adequately
treat incorrect as well as correct data. Among the options common sense would
suggest to a programmer of a property tax base evaluation:
(1) Each type of property has a certain plausible maximum and minimum value.
The maximum plausible value for a house might be 120% of the previous year's
highest assessed value.
(2) Such maxima and minima might be set by neighborhoods; a single-family house
in a slum is unlikely to be worth more than $100,000, while many a house in
Shaker Heights might top $1,000,000.
(3) If the previous year's evaluation of the same property is available on a
computer file, plausible maxima and minima might be (say) 20% above and below
the old valuation.
(4) Depending on the degree of implausibility, the computer might request
confirmation of the input by the typist:
``Valuation increased by 35% in one year. Is this confirmed?
Type Y or N.''
It might demand credentials:
``New house evaluated at $2,800,000. Evaluations over
$1,000,000 must be confirmed by county assessor or deputy.
Please enter password of confirming officer, or the word
POSTPONE.''
It might simply refuse to accept certain data.
``Valuation of single family home over $10,000,000 does not compute.
Don't mess with me or I'll raise your electric bill.''
In addition to the willingness of Cleveland's program to accept bad data, it
apparently had a second failing; the left hand did not know what the right one was
doing. The Sprenger assessment was corrected for the purposes of calculating the
Sprenger's tax bill, but not for the purpose of calculating the city's tax base.
Part of the discipline of data base management (the handling of large files of
repeatedly used data) is to avoid using multiple copies of what should be the
same data in different places; such multiple copies can lead to internal
inconsistency in the data base.
The use of a numerical code to indicate the type of building is an open
invitation to the kind of error that occured. Why not use ``R,'' rather
than 5100, to mean residence?
A good programmer should always ask "What happens to the world if someone enters
bad data into my program, five years in the future, when I've gone away to the
job that finally pays me what I'm worth? Will the program be likely to detect
bad data? Will the results of undetected bad data seem plausible? How likely
are bad data? How catastropic are the consequences?''
If you ever go to work as a programmer for Cuyahoga County, please tell them
what I said. And try to arrange to be paid by a handwritten paycheck.
(On the other hand, if you opt for a computerized paycheck, you might get one
for $510,035,000. There's always that chance.)
``Did you hear the one about the river catching fire in Cleveland?''
``Yes.''